import { Injectable } from '@nestjs/common';
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
import { Like, Repository, In, EntityManager, Between, DataSource } from 'typeorm';
import { instanceToPlain, plainToInstance } from 'class-transformer';
import { genSalt, hash, compare, genSaltSync, hashSync } from 'bcryptjs';

import { ProjectEntity } from './entities/project.entity'
import { MonitoringLogEntity } from './entities/monitoring-log.entity'

import { ResultData } from '../common/utils/result'
import { RandomNumber, dateTimeSqlQuery } from '../common/utils/utils'
import { AppHttpCode } from '../common/enums/code.enum'

import { CreateProjectDto } from './dto/create-project.dto'
import { UpdateProjectDto } from './dto/update-project.dto'
import { FindProjectListDto } from './dto/find-project-list.dto'
import { FindLogListDto } from './dto/find-log-list.dto'

import { OperalogService } from 'src/system/operalog/operalog.service'

@Injectable()
export class ProjectService {
  constructor(
    @InjectRepository(ProjectEntity)
    private readonly projectRepo: Repository<ProjectEntity>,
    @InjectRepository(MonitoringLogEntity)
    private readonly monitoringLogRepo: Repository<MonitoringLogEntity>,
    
    @InjectEntityManager()
    private readonly projectManager: EntityManager,
    private readonly operalogService: OperalogService,
    private readonly dataSource: DataSource
  ) {}

  async findOneById(id: string): Promise<ProjectEntity> {
    let project = await this.projectRepo.findOne({ where: { id } })
    project = plainToInstance(ProjectEntity, { ...project }, { enableImplicitConversion: true })
    return project
  }

  async findOneLogById(id: string): Promise<MonitoringLogEntity> {
    let monitoringLog = await this.monitoringLogRepo.findOne({ where: { id } })
    monitoringLog = plainToInstance(MonitoringLogEntity, { ...monitoringLog }, { enableImplicitConversion: true })
    return monitoringLog
  }

  /** 创建项目 */
  async create(createProjectDto: CreateProjectDto, account: String, ip: String) {
    /** 添加32位数密钥，在第三方项目添加日志监控时候需要带上这串密钥 */
    createProjectDto.projectKey = RandomNumber(32);
    const project = plainToInstance(ProjectEntity, createProjectDto, { ignoreDecorators: true });
    const result = await this.projectManager.transaction(async (transactionalEntityManager) => {
      return await transactionalEntityManager.save<ProjectEntity>(project)
    })

    /** 创建项目后插入操作日志 */
    if (ip.indexOf('::ffff:') !== -1) {
      ip = ip.substring(7)
    }
    this.operalogService.create({
      systemMenu: '项目监控',
      operaModule: `创建项目：【${project.projectName}】`,
      operaName: account,
      operaIp: ip,
      status: 1
    })
    return ResultData.ok(instanceToPlain(result));
  }

  /** 查询项目分页查询 */
  async findList(dto: FindProjectListDto) {
    const { page, size, status, projectName } = dto;
    const where = {
      ...(status ? { status } : null),
      ...(projectName ? { projectName: Like(`%${projectName}%`) } : null),
      isDelete: 1
    }
    const project = await this.projectRepo.findAndCount({
      where,
      order: { id: 'DESC' },
      skip: size * (page - 1),
      take: size,
    })
    return ResultData.ok({ list: instanceToPlain(project[0]), total: project[1] })
  }

  /** 查询单条项目信息 */
  async findOne(id: string) {
    const project = await this.findOneById(id)
    if (!project) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '该项目不存在或已删除')
    return ResultData.ok(instanceToPlain(project))
  }

  /** 更新项目状态 */
  async update(updateProjectDto: UpdateProjectDto, account: String, ip: String): Promise<ResultData> {
    /** 查询项目是否存在 */
    const existing = await this.findOneById(updateProjectDto.id)
    if (!existing) ResultData.fail(AppHttpCode.USER_NOT_FOUND, '当前项目不存在或已删除')
    /** 更新项目 */
    const { affected } = await this.projectManager.transaction(async (transactionalEntityManager) => {
      return await transactionalEntityManager.update<ProjectEntity>(ProjectEntity, existing.id, existing)
    })
    if (!affected) ResultData.fail(AppHttpCode.SERVICE_ERROR, '更新失败，请稍后尝试');

    /** 更新项目状态后插入操作日志 */
    if (ip.indexOf('::ffff:') !== -1) {
      ip = ip.substring(7)
    }
    this.operalogService.create({
      systemMenu: '项目监控',
      operaModule: `更新项目：【${existing.projectName}】`,
      operaName: account,
      operaIp: ip,
      status: 1
    })
    return ResultData.ok()
  }

  /** 更新项目状态 */
  async updateStatus(projectId: string, status: 0 | 1, account: String, ip: String): Promise<ResultData> {
    /** 查询项目是否存在 */
    const existing = await this.findOneById(projectId)
    if (!existing) ResultData.fail(AppHttpCode.USER_NOT_FOUND, '当前项目不存在或已删除')
    /** 更新项目状态 */
    const { affected } = await this.projectManager.transaction(async (transactionalEntityManager) => {
      return await transactionalEntityManager.update<ProjectEntity>(ProjectEntity, projectId, { id: projectId, status })
    })
    if (!affected) ResultData.fail(AppHttpCode.SERVICE_ERROR, '更新失败，请稍后尝试');

    /** 更新项目状态后插入操作日志 */
    if (ip.indexOf('::ffff:') !== -1) {
      ip = ip.substring(7)
    }
    this.operalogService.create({
      systemMenu: '项目监控',
      operaModule: `更新项目状态：【${existing.projectName}】`,
      operaName: account,
      operaIp: ip,
      status: 1
    })
    return ResultData.ok()
  }

  /** 逻辑删除项目 */
  async updateDelete(projectId: string, isDelete: 0 | 1, account: String, ip: String): Promise<ResultData> {
    /** 查询项目是否存在 */
    const existing = await this.findOneById(projectId)
    if (!existing) ResultData.fail(AppHttpCode.USER_NOT_FOUND, '当前项目不存在或已删除')
    /** 逻辑删除项目 */
    const { affected } = await this.projectManager.transaction(async (transactionalEntityManager) => {
      return await transactionalEntityManager.update<ProjectEntity>(ProjectEntity, projectId, { id: projectId, isDelete })
    })
    if (!affected) ResultData.fail(AppHttpCode.SERVICE_ERROR, '删除失败，请稍后尝试');

    /** 逻辑删除成功后插入操作日志 */
    if (ip.indexOf('::ffff:') !== -1) {
      ip = ip.substring(7)
    }
    this.operalogService.create({
      systemMenu: '项目监控',
      operaModule: `更新项目状态：【${existing.projectName}】`,
      operaName: account,
      operaIp: ip,
      status: 1
    })
    return ResultData.ok()
  }

  /** 查询项目监控分页查询 */
  async findLogList(dto: FindLogListDto) {
    const { page, size, projectName, createDate } = dto;
    let queryDate;
    
    if (createDate) {
      let sDate = `${createDate}.000000`;
      let eDate = `${createDate}`;
      let endDateData = dateTimeSqlQuery(eDate)
      endDateData = `${createDate}.999999`;
      queryDate = Between(new Date(sDate), new Date(endDateData))
    } else {
      queryDate = null
    }
    const where = {
      ...(projectName ? { projectName: Like(`%${projectName}%`) } : null),
      ...(createDate ? { createDate: queryDate } : null)
    }

    const monitoringLog = await this.monitoringLogRepo.findAndCount({
      where,
      order: { id: 'DESC' },
      skip: size * (page - 1),
      take: size,
    })
    return ResultData.ok({ list: instanceToPlain(monitoringLog[0]), total: monitoringLog[1] })
  }

  /** 查询项目监控信息 */
  async findLogOne(id: string) {
    let monitoringLog = await this.findOneLogById(id)
    if (!monitoringLog) return ResultData.fail(AppHttpCode.USER_NOT_FOUND, '该项目不存在或已删除')
    return ResultData.ok(instanceToPlain(monitoringLog))
  }

}
